package dian.qing.li.demo.security.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import dian.qing.li.demo.security.constants.Constants;
import dian.qing.li.demo.security.dto.RequestDto;
import dian.qing.li.demo.security.mapper.RequestMapper;
import dian.qing.li.demo.security.service.AppReadyService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.MediaTypeExpression;
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: liqingdian
 **/
@Service("dian.qing.li.demo.security.service.impl.RequestReadyServiceImpl")
public class RequestReadyServiceImpl implements AppReadyService {

    private static final AntPathMatcher MATCHER = new AntPathMatcher();

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired
    private RequestMapper requestMapper;

    @Override
    public void ready() {
        List<RequestDto> requestList = this.getRequestList();
        List<RequestDto> requestAll = requestMapper.selectList(new QueryWrapper<>());
        ArrayList<RequestDto> updateList = new ArrayList<>();

        Iterator<RequestDto> currentIter = requestList.iterator();
        while (currentIter.hasNext()) {
            RequestDto current = currentIter.next();
            Iterator<RequestDto> findIter = requestAll.iterator();
            while (findIter.hasNext()) {
                RequestDto next = findIter.next();
                boolean match = MATCHER.match(current.getReqValue(), next.getReqValue());
                boolean method = StringUtils.equalsIgnoreCase(current.getReqMethod(), next.getReqMethod());
                if (match && method) {
                    current.setId(next.getId());
                    current.setVersion(next.getVersion());
                    updateList.add(current);
                    currentIter.remove();
                    findIter.remove();
                }
            }
        }
        List<Integer> deleteIds = requestAll.stream().map(RequestDto::getId).collect(Collectors.toList());
        for (Integer deleteId : deleteIds) {
            requestMapper.deleteById(deleteId);
        }
        for (RequestDto update : updateList) {
            requestMapper.updateById(update);
        }
        for (RequestDto insert : requestList) {
            requestMapper.insert(insert);
        }
    }

    private List<RequestDto> getRequestList() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        ArrayList<RequestDto> requestList = Lists.newArrayList();
        RequestDto request;
        for (Map.Entry<RequestMappingInfo, HandlerMethod> m : handlerMethods.entrySet()) {
            request = new RequestDto();
            Set<String> patterns = m.getKey().getPatternsCondition().getPatterns();
            String reqValue = patterns.stream().findFirst().orElse("/");
            if (StringUtils.equals("/", reqValue) || StringUtils.equals("/error", reqValue)) {
                continue;
            }
            request.setReqValue(reqValue);
            request.setReqName(m.getKey().getName());
            String method = m.getKey().getMethodsCondition().getMethods().stream().map(RequestMethod::toString).findFirst().orElse("GET");
            request.setReqMethod(method);
            request.setReqPath(reqValue);
            Set<String> params = m.getKey().getParamsCondition().getExpressions().stream().map(NameValueExpression::getName).collect(Collectors.toSet());
            request.setReqParams(JSON.toJSONString(params));
            Set<String> headers = m.getKey().getHeadersCondition().getExpressions().stream().map(NameValueExpression::getName).collect(Collectors.toSet());
            request.setReqHeaders(JSON.toJSONString(headers));
            Set<String> consumes = m.getKey().getConsumesCondition().getConsumableMediaTypes().stream().map(MediaType::toString).collect(Collectors.toSet());
            request.setReqConsumes(JSON.toJSONString(consumes));
            Set<String> produces = m.getKey().getProducesCondition().getExpressions().stream().map(MediaTypeExpression::toString).collect(Collectors.toSet());
            request.setReqProduces(JSON.toJSONString(produces));
            request.setReference(m.getValue().toString());
            request.setRemark(m.getValue().toString());
            Deprecated deprecated = m.getValue().getMethodAnnotation(Deprecated.class);
            if (ObjectUtils.isNotEmpty(deprecated)) {
                request.setDisabled(Constants.YES);
            }
            request.setUpdateBy("admin");
            request.setCreateBy("admin");
            requestList.add(request);
        }
        return requestList;
    }
}
